﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace FirstFloor.Reversi.Controls
{
    /// <summary>
    /// Represents the reversi board.
    /// </summary>
    public class Board
        : ControlBase
    {
        /// <summary>
        /// Occurs when the link is clicked
        /// </summary>
        public event EventHandler<EventArgs> LinkClick;
        /// <summary>
        /// The board length
        /// </summary>
        public const int BoardLength = 8;

        /// <summary>
        /// Occurs when a piece is selected.
        /// </summary>
        public event EventHandler<LocationEventArgs> PieceSelected;

        private const int PieceSize = 43;

        private Piece[,] board;

        /// <summary>
        /// Initializes a new instance of the <see cref="Board"/> class.
        /// </summary>
        public Board()
        {
            Initialize();

            TextBlock link = (TextBlock)FindName("link");
            link.MouseLeftButtonDown += new MouseEventHandler(OnLinkClick);
        }

        private void Initialize()
        {
            Canvas control = (Canvas)Control;

            this.board = new Piece[BoardLength, BoardLength];

            for (int x = 0; x < BoardLength; x++) {
                for (int y = 0; y < BoardLength; y++) {
                    Piece piece = new Piece(x, y);
                    this.board[x, y] = piece;

                    Image image = new Image();
                    image.Source = piece.GetImageUrl();
                    image.SetValue<string>(Canvas.NameProperty, piece.GetImageElementName());
                    image.SetValue<int>(Canvas.LeftProperty, x * PieceSize);
                    image.SetValue<int>(Canvas.TopProperty, y * PieceSize);

                    image.MouseLeftButtonUp += new MouseEventHandler(image_MouseLeftButtonUp);
                    image.MouseEnter += new MouseEventHandler(image_MouseEnter);
                    image.MouseLeave += new EventHandler(image_MouseLeave);

                    control.Children.Add(image);
                }
            }
        }

        private Piece GetBoardPiece(Image image)
        {
            Location location = Piece.GetLocationFromImageName(image.Name);
            return GetBoardPiece(location.X, location.Y);
        }

        private Piece GetBoardPiece(int x, int y)
        {
            if (x < 0 || x > BoardLength) {
                throw new ArgumentOutOfRangeException("x");
            }
            if (y < 0 || y > BoardLength) {
                throw new ArgumentOutOfRangeException("y");
            }

            return this.board[x, y];
        }

        private void UpdateBoardPiece(Piece piece, PieceColor color, bool marked, bool selectable)
        {
            bool updated = piece.Color != color || piece.Marked != marked || piece.Selectable != selectable;

            if (updated) {
                piece.Color = color;
                piece.Marked = marked;
                piece.Selectable = selectable;

                Image image = (Image)FindName(piece.GetImageElementName());
                image.Source = piece.GetImageUrl();
                image.Cursor = selectable ? Cursors.Hand : Cursors.Default;
            }
        }

        /// <summary>
        /// The resource name containg the xaml used to initialize the actual object
        /// </summary>
        /// <value></value>
        protected override string XamlResourceName
        {
            get { return "FirstFloor.Reversi.Source.Controls.Board.xaml"; }
        }

        /// <summary>
        /// Gets or sets the <see cref="FirstFloor.Reversi.PieceColor"/> at specified position.
        /// </summary>
        /// <value></value>
        public PieceColor this[int x, int y]
        {
            get
            {
                Piece piece = GetBoardPiece(x, y);

                return piece.Color;
            }
            set
            {
                Piece piece = GetBoardPiece(x, y);

                UpdateBoardPiece(piece, value, piece.Marked, piece.Selectable);
            }
        }

        /// <summary>
        /// Gets the number of pieces having specified color.
        /// </summary>
        /// <param name="color">The color.</param>
        /// <returns></returns>
        public int GetPieceCount(PieceColor color)
        {
            int count = 0;
            for (int i = 0; i < BoardLength; i++) {
                for (int j = 0; j < BoardLength; j++) {
                    if (this.board[i, j].Color == color) {
                        count++;
                    }
                }
            }
            return count;
        }

        /// <summary>
        /// Clears the board.
        /// </summary>
        public void Clear()
        {
            for (int x = 0; x < BoardLength; x++) {
                for (int y = 0; y < BoardLength; y++) {
                    UpdateBoardPiece(GetBoardPiece(x, y), PieceColor.None, false, false);
                }
            }
        }

        /// <summary>
        /// Clears the marks.
        /// </summary>
        public void ClearMarks()
        {
            for (int x = 0; x < BoardLength; x++) {
                for (int y = 0; y < BoardLength; y++) {
                    Piece piece = GetBoardPiece(x, y);
                    UpdateBoardPiece(piece, piece.Color, false, piece.Selectable);
                }
            }
        }

        /// <summary>
        /// Set all pieces unselectable.
        /// </summary>
        public void ClearSelectables()
        {
            for (int x = 0; x < BoardLength; x++) {
                for (int y = 0; y < BoardLength; y++) {
                    Piece piece = GetBoardPiece(x, y);
                    UpdateBoardPiece(piece, piece.Color, piece.Marked, false);
                }
            }
        }

        /// <summary>
        /// Clears the marks and selectable pieces.
        /// </summary>
        public void ClearMarksAndSelectables()
        {
            for (int x = 0; x < BoardLength; x++) {
                for (int y = 0; y < BoardLength; y++) {
                    Piece piece = GetBoardPiece(x, y);
                    UpdateBoardPiece(piece, piece.Color, false, false);
                }
            }
        }

        /// <summary>
        /// Marks or unmarks the specified piece.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="mark">if set to <c>true</c> mark.</param>
        public void MarkPiece(int x, int y, bool mark)
        {
            Piece piece = GetBoardPiece(x, y);

            UpdateBoardPiece(piece, piece.Color, mark, piece.Selectable);
        }

        /// <summary>
        /// Set the specified piece selectable.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="selectable">if set to <c>true</c> selectable.</param>
        public void SetSelectable(int x, int y, bool selectable)
        {
            Piece piece = GetBoardPiece(x, y);

            UpdateBoardPiece(piece, piece.Color, piece.Marked, selectable);
        }

        /// <summary>
        /// Handles the MouseLeftButtonUp event of the image control.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        protected virtual void image_MouseLeftButtonUp(object sender, MouseEventArgs args)
        {
            Image image = sender as Image;
            Location location = Piece.GetLocationFromImageName(image.Name);
            if (PieceSelected != null) {
                PieceSelected(this, new LocationEventArgs(location));
            }
        }

        /// <summary>
        /// Handles the MouseEnter event of the image control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void image_MouseEnter(object sender, MouseEventArgs e)
        {
            Piece piece = GetBoardPiece(sender as Image);
            if (piece.Selectable) {


            }
        }

        /// <summary>
        /// Handles the MouseLeave event of the image control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void image_MouseLeave(object sender, EventArgs e)
        {
            Piece piece = GetBoardPiece(sender as Image);
            if (piece.Selectable) {
            }
        }

        private void OnLinkClick(object sender, EventArgs e)
        {
            if (LinkClick != null) {
                LinkClick(this, EventArgs.Empty);
            }
        }
    }
}
